package com.ticket.sass.admin.util;

import com.ticket.sass.admin.service.impl.TicketServerConfigServiceImpl;
import com.ticket.sass.common.entity.tenant.FilmScheduleEntity;
import com.ticket.sass.common.entity.tenant.HallEntity;
import com.ticket.sass.common.entity.tenant.SeatLockMapEntity;
import com.ticket.sass.common.entity.tenant.TicketServerConfigEntity;
import com.ticket.sass.common.exception.enums.ErrorCode;
import com.ticket.sass.common.exception.exception.BadRequestException;
import com.ticket.sass.common.rpc.dto.input.ticketserver.OrderTicketRefundInput;
import com.ticket.sass.common.rpc.dto.input.ticketserver.ScheduleInput;
import com.ticket.sass.common.rpc.dto.output.ResultWrapOutput;
import com.ticket.sass.common.rpc.ticketserver.Client;
import com.ticket.sass.common.ticketserver.dto.output.HallOutput;
import com.ticket.sass.common.ticketserver.dto.output.OrderTicketRefundOutput;
import com.ticket.sass.common.ticketserver.dto.output.ScheduleOutput;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.TypeToken;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 优化后的 ScheduleDataUtil 类，增加异常处理
 * @author ywb
 * @date 2024-03-21 18:27
 */
@Slf4j
@Component
public class ScheduleDataUtil {
    private final TicketServerConfigServiceImpl service;
    private final ModelUtil modelUtil;
    
    public ScheduleDataUtil(TicketServerConfigServiceImpl service, ModelUtil modelUtil) {
        this.service = service;
        this.modelUtil = modelUtil;
    }
    
    // 通用的RPC调用方法
    private <T> ResultWrapOutput<T> callRpc(Long cinemaId, Function<Client, Mono<ResultWrapOutput<T>>> rpcFunction, String errorMessage) {
        Client rpcTicketServer = new Client();
        TicketServerConfigEntity byCinemaId = service.findByCinemaId(cinemaId);
        if (byCinemaId == null) {
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, "影院配置有误");
        }
        
        ResultWrapOutput<T> resultWrapOutput;
        try {
            Mono<ResultWrapOutput<T>> mono = rpcFunction.apply(rpcTicketServer.setConfig(byCinemaId));
            resultWrapOutput = mono.block();
        } catch (WebClientRequestException e) {
            log.error("RPC连接失败", e);
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, "RPC服务未启动，无法连接");
        } catch (Exception e) {
            log.error(errorMessage, e);
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, errorMessage);
        }
        
        log.info("本次调用接口返回【{}】", resultWrapOutput);
        
        if (resultWrapOutput == null || resultWrapOutput.getData() == null) {
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, "暂无数据");
        }
        
        return resultWrapOutput;
    }
    
    public List<FilmScheduleEntity> getSchedules(Long cinemaId) {
        ResultWrapOutput<ScheduleOutput> resultWrapOutput = callRpc(cinemaId,
            client -> client.schedules(new ScheduleInput(LocalDate.now().toString(), LocalDate.now().plusDays(20).toString()), null),
            "获取排期配置错误");
        
        List<ScheduleOutput.Schedule> list = Objects.requireNonNull(resultWrapOutput.getData()).getList();
        if (list.isEmpty()) {
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, "暂无排期");
        }
        
        TicketServerConfigEntity byCinemaId = service.findByCinemaId(cinemaId);
        Long tenantId = byCinemaId.getTenantId();
        
        return convertToFilmScheduleEntities(list, tenantId);
    }
    
    public List<HallEntity> getHalls(Long cinemaId) {
        ResultWrapOutput<HallOutput> resultWrapOutput = callRpc(cinemaId,
            client -> client.halls(null),
            "获取影厅信息错误");
        
        List<HallOutput.Hall> list = Objects.requireNonNull(resultWrapOutput.getData()).getList();
        if (list.isEmpty()) {
            throw new BadRequestException(ErrorCode.BUSINESS_ERROR, "暂无影厅信息");
        }
        
        List<HallEntity> entities = modelUtil.listToList(list, new TypeToken<List<HallEntity>>(){}.getType());
        return entities;
    }
    
    public boolean refundTicket(String code, String orderNo ,String sessionCode, List<SeatLockMapEntity> byLockId, Long cinemaId) {
        List<OrderTicketRefundInput.Seat> collect = byLockId.stream().map(l -> new OrderTicketRefundInput.Seat(l.getSeatId(), l.getSeatCode())).collect(Collectors.toList());
        
        OrderTicketRefundInput input = new OrderTicketRefundInput(code, orderNo, sessionCode, collect);
        log.info("退票入参【{}】", input);
        ResultWrapOutput<OrderTicketRefundOutput> resultWrapOutput = callRpc(cinemaId,
            client -> client.orderTicketRefund(input, null),
            "退票失败");
        
        return Objects.requireNonNull(resultWrapOutput.getData()).getSucceeded();
    }
    
    public static List<FilmScheduleEntity> convertToFilmScheduleEntities(List<ScheduleOutput.Schedule> scheduleList, Long tenantId) {
        if (scheduleList.isEmpty()) {
            return new ArrayList<>();
        }
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        List<FilmScheduleEntity> filmScheduleEntities = new ArrayList<>();
        for (ScheduleOutput.Schedule schedule : scheduleList) {
            FilmScheduleEntity filmScheduleEntity = new FilmScheduleEntity();
            filmScheduleEntity.setScheduleKey(schedule.getScheduleKey());
            filmScheduleEntity.setSessionCode(schedule.getSessionCode());
            filmScheduleEntity.setHall(schedule.getHallName());
            filmScheduleEntity.setFilmName(schedule.getFilmName());
            filmScheduleEntity.setLang(schedule.getLanguage());
            filmScheduleEntity.setStartTime(LocalDateTime.parse(schedule.getStartTime(), formatter));
            filmScheduleEntity.setEndTime(LocalDateTime.parse(schedule.getEndTime(), formatter));
            filmScheduleEntity.setHallCode(schedule.getHallCode());
            filmScheduleEntity.setFilmCode(schedule.getFilmCode());
            filmScheduleEntity.setLowestPrice(BigDecimal.valueOf(schedule.getLowestPrice()));
            filmScheduleEntity.setStandardPrice(BigDecimal.valueOf(schedule.getStandPrice()));
            filmScheduleEntity.setServiceFee(BigDecimal.valueOf(schedule.getServiceFee()));
            filmScheduleEntity.setSeatVersion(schedule.getSeatVersion());
            filmScheduleEntity.setLastUpdateTime(schedule.getUpdateTime());
            filmScheduleEntity.setVersion(schedule.getFilmVersion());
            filmScheduleEntity.setCreatedAt(LocalDateTime.now());
            filmScheduleEntity.setUpdatedAt(LocalDateTime.now());
            filmScheduleEntity.setTenantId(tenantId);
            filmScheduleEntities.add(filmScheduleEntity);
        }
        return filmScheduleEntities;
    }
}
